----------------------------------------------------------------------------------
-- Company:        RIT
-- Engineer:       Sam Skalicky
-- 
-- Create Date:    17:49:05 12/11/2009 
-- Design Name:    MSD P10662
-- Module Name:    Device - Behavioral 
-- Project Name:   Inout
-- Target Devices: Spartan 6 LXT
-- Tool versions:  QuestaSim-64 6.4c
-- Description:    This a bi-directional I2C/Two-wire port implementation in VHDL
--
-- Notes: Data is the bi-directional port, Clk is typeset as an inout in the idea that
-- the clock could be generated to this port (Master device) or recieved from this port
-- in the case of a Slave device. However it is not meant to be a bi-directional port
--
-- Dependencies:   None
--
-- Revision: 1.0
-- Revision 0.01 - File Created
-- Additional Comments: 
--
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

Library UNISIM;
use UNISIM.vcomponents.all;

entity I2Cslave_debug is
    Port ( Clk : in  STD_LOGIC;
           FPGAClk : in std_logic;
           rid_valid : out std_logic;
           register_id : out std_logic_vector(7 downto 0);
           Data_to_send : in std_logic_vector(15 downto 0);
           dout_valid : out std_logic; 
           Data_out : out std_logic_vector(15 downto 0);
           Direction : out std_logic;
           I2C_toBuffer : out std_logic;
           I2C_fromBuffer : in std_logic;
			  My_I2CAddress : out std_logic_vector(6 downto 0);
			  Recieved_Address : out std_logic_vector(7 downto 0);
			  Address_Valid : out std_logic);
end I2Cslave_debug;

architecture Behavioral of I2Cslave_debug is

	signal Dir,out_i2c,in_i2c : std_logic:='1';
	type states is (idle,recieve,addrCheck,ack,write,read,recAck);
  signal pstate,nstate : states:=idle;
  signal counter : std_logic_vector(5 downto 0);
  signal shiftReg,i2cAddr : std_logic_vector(7 downto 0):="00000000";
  signal rID : std_logic_vector(7 downto 0):=(others=>'0');
  signal rData,sData : std_logic_vector(15 downto 0):=(others=>'0');
  signal writeCount : natural:=0;
  signal byte : natural:=7;
  signal myAddress : std_logic_vector(6 downto 0):="1011000";
  signal ack_count : std_logic_vector(11 downto 0):=(others=>'0');
  signal delay_count : std_logic_vector(15 downto 0):=(others=>'0');
  signal edge,ClkEdge : std_logic_vector(1 downto 0):="11";
  signal prevData,DataFallingEdge,DataRisingEdge,ClkFallingEdge,ClkRisingEdge,prevClk : std_logic:='0';
  signal ridvalid,doutvalid,done,Data,addrValid : std_logic:='0';
  
begin
  
  sData<=Data_to_send;
  Direction<=Dir;
  register_id<=rID;
  Data_out<=rData;
  rid_valid<=ridvalid;
  dout_valid<=doutvalid;
  I2C_toBuffer<=in_i2c;
  out_i2c<=I2c_fromBuffer;
  Data<=out_i2c;
  My_I2CAddress<=myAddress;
  Recieved_Address<=i2cAddr;
  Address_Valid<=addrValid;

  --***Moved to external entity
	--IOBUF: Single-ended Bi-directional Buffer All devices
	--Xilinx HDL Libraries Guide version 8.1i
	--IOBUF_inst : IOBUF 
	--	generic map (
	--		DRIVE => 12, 
	--		IBUF_DELAY_VALUE => "0", 	-- Specify the amount of added input delay for buffer, "0"-"16" (Spartan-3E only)
	--		IFD_DELAY_VALUE => "AUTO", -- Specify the amount of added delay for input register, "AUTO", "0"-"8" (Spartan-3E only)
	--		IOSTANDARD => "DEFAULT",
	--		SLEW => "SLOW") 
	--	port map (
	--		O => out_i2c, 		-- Buffer output
	--		IO => Data, 	-- Buffer inout port (connect directly to top-level port)
	--		I => in_i2c, 		-- Buffer input
	--		T => Dir		-- 3-state enable input
	--	); 
	--End of IOBUF_inst instantiation

  ------------------------------
  -- I2C Data edge detector
  ------------------------------
  process(Data,FPGAClk)
  begin
    if rising_edge(FPGAClk) then
	   edge<=edge(0) & Data;
		
		if edge = "00" and prevData = '1' then
		  DataFallingEdge <= '1';
		  DataRisingEdge <='0';
		  prevData<='0';
		elsif edge = "11" and prevData = '0' then
		  prevData<='1';
		  DataFallingEdge<='0';
		  DataRisingEdge<='1';
		else
		  DataFallingEdge<='0';
		  DataRisingEdge<='0';
		end if;
	 end if;
  end process;

  ------------------------------
  -- I2C Clk edge detector
  ------------------------------
  process(Clk,FPGAClk)
  begin
    if rising_edge(FPGAClk) then
	   ClkEdge<=ClkEdge(0) & Clk;
		
		if ClkEdge = "00" and prevClk = '1' then
		  ClkFallingEdge <= '1';
		  ClkRisingEdge<='0';
		  prevClk<='0';
		elsif ClkEdge = "11" and prevClk = '0' then
		  prevClk<='1';
		  ClkFallingEdge<='0';
		  ClkRisingEdge<='1';
		else
		  ClkFallingEdge<='0';
		  ClkRisingEdge<='0';
		end if;
	 end if;
  end process;

  ------------------------------
  -- Control
  ------------------------------
  process (FPGAClk,Clk,Data,nstate)
  begin
    if rising_edge(FPGAClk) then
      --check stop bit to reset to idle
      if Clk = '1' then
        if DataRisingEdge = '1' then
          nstate<=idle;
          pstate<=nstate;
          if done = '0' then
            doutvalid<='0';
			      ridvalid<='0';
			      addrValid<='0';
			    end if;
        end if;
      end if;
      
    --state machine
	   case nstate is
		   when idle =>	--state changes when start bit received
		     if Clk = '1' then
			     if DataFallingEdge='1' then
			       doutvalid<='0';
			       ridvalid<='0';
			       addrValid<='0';
				     nstate<=recieve;
				     pstate<=nstate;
				   end if;
			   end if;
		   when recieve =>		--receive 8 bits
		     if counter = "001000" then
		       if pstate = idle then
		       nstate<=addrCheck;
		       pstate<=nstate;
		       end if;
		     elsif counter = "010000" or counter = "011001" or counter = "100010" then
		       nstate<=ack;
		       pstate<=nstate;
		     end if;
	    	when addrCheck =>
			   if myAddress = i2cAddr(7 downto 1) then
			     nstate<=ack;
			     pstate<=nstate;
			     addrValid<='1';
			   else
			     nstate<=idle;
			     pstate<=nstate;
			   end if;
		   when ack =>
		     if counter = "010001" then
		       ridvalid<='1';
		     elsif counter = "100010" then
		       doutvalid<='1';
		     end if;
		     
		     if ack_count = X"164" and counter = "001000" then
      		       nstate<=recieve;
      		       pstate<=nstate;
 		    elsif ack_count = X"164" and counter = "010001" then
		      if i2cAddr(7) = '0' then
   		       nstate <= read;
   		       pstate<=nstate;
		      else
		        nstate <= recieve;
		        pstate<=nstate;
		      end if;
		    elsif (counter = "011001" or counter = "011010") and (ack_count = X"164") then
		      nstate<=recieve;
		      pstate<=nstate;
		    elsif (counter = "100010") and ack_count = X"164" then
		      nstate<=write;
		      pstate<=nstate;
		      done<='1';
		    end if;
		   when write =>
		     nstate<=idle;
		     pstate<=nstate;
		   when read =>
 		     if delay_count > X"0F9C" then
 		       nstate<=recAck;
 		       pstate<=nstate;
 		     end if;
		   when recAck =>
		     if delay_count > X"01EF" then
		       nstate<=idle;
		       pstate<=nstate;
		     end if;
	   end case;
	  end if;                         
  end process;
  ------------------------------
  
  
  ------------
  --Datapath
  ------------
  process (FPGAClk,nstate,Clk,counter,shiftReg)
    
  begin
   if rising_edge(FPGAClk) then
    case nstate is
       when idle =>
   	     counter<="000000";
		   when recieve =>		--receive 8 bits
		     ack_count<=X"000";
		     if ClkRisingEdge ='1' then
		       counter<=counter + 1;
		       shiftReg<=shiftReg(6 downto 0) & out_i2c;
		     end if;
		     
		     if counter = "001000" then
		       i2cAddr<=shiftReg;
		     elsif counter = "010000" then
		       rID<=shiftReg;
		       ack_count<=X"000";
		     elsif counter = "011001" then
		       rData(15 downto 8)<=shiftReg;
		     elsif counter = "100010" then
		       rData(7 downto 0) <=shiftReg;
		     end if;
	    	when addrCheck =>
	    	  null;
		   when ack =>
		     if ClkFallingEdge = '1' then
		       ack_count<=X"001";
		     end if;
		     if counter = "010000" then
		       counter <= "010001";
		     elsif counter = "011001" then
		       counter <= "011010";
		     end if;
		       case ack_count is
		         when X"000" => null;
		         when X"002" =>
		           Dir<='0';
		           in_i2c<='0';
		           ack_count<=ack_count + 1;
		         when X"163" =>
		           Dir<='1';
		           ack_count<=ack_count+1;
		         --when X"1E0" =>
		         --  ack_count<=X"000";
		         when others =>
		           ack_count<= ack_count + 1;  
		       end case;
		   when write =>
		     null;
		   when read =>
		     if ClkFallingEdge = '1' then
    		       delay_count<=X"0001";
    		       
    		       in_i2c<=shiftReg(0);
    		       shiftReg<='0'&shiftReg(7 downto 1);
 		     end if;		
 		     if ClkFallingEdge = '1' and (delay_count < X"0F9C") and (delay_count >= X"0021") then
		         writeCount<=writeCount + 1;
		     end if;
		     
		       case delay_count is
		         when X"0000" => 
		           shiftReg<=sData(byte downto (byte-7));
		         when X"0020" =>
		           Dir<='0';
		           delay_count<=delay_count + 1;
		         when X"0F9C" =>
		           Dir<='1';
		           delay_count<=delay_count + 1;
		         when X"0F9D" =>
		           delay_count<=X"0000";
		         when others =>
		           delay_count<=delay_count + 1;
		       end case;
		   when recAck =>
         delay_count<=delay_count + 1;
	   end case;
	  end if; 
  end process;
end Behavioral;